docs: add docs/stress-ecs/z.md [ABCA-727]#51
Open
isadeks wants to merge 490 commits into
Open
Conversation
…mples#350) (aws-samples#351) Least-privilege bootstrap v1.2.0 was missing IAM for integration secrets, SQS DLQs, CloudFront OAC, Lambda layers, event-source mappings, and app S3 buckets — causing mid-deploy AccessDenied on custom bootstrap. Adds a resource-action map and synth-coverage test, updates DEPLOYMENT_ROLES.md, and documents the check in review_pr. Co-authored-by: bgagent <bgagent@noreply.github.com> Co-authored-by: Cursor <cursoragent@cursor.com>
…e issues (aws-samples#247 UX.3b) An @bgagent comment on a PLAIN Linear issue (no orchestration epic) that ABCA opened a PR for now iterates on that PR with the same 👀-on-receipt / threaded-reply-on-completion ack — not just epic sub-issues. Resolution (robust GSI path, deterministic from ABCA's own record): - TaskTable gains a sparse LinearIssueIndex GSI (PK linear_issue_id, SK created_at, INCLUDE pr_url/pr_number/status/repo/user_id/ channel_metadata). Sparse = Linear-origin tasks only. - create-task-core hoists linear_issue_id top-level (a GSI cannot key off the nested channel_metadata map); TaskRecord.linear_issue_id. - shared/linear-task-by-issue.ts: resolveTaskByLinearIssue (newest task for an issue) + prNumberFromTask. Trigger (linear-webhook-processor): - handleCommentTrigger falls through to handleStandaloneCommentTrigger when the issue isn't a started orchestration child (no parent / not an orchestration / un-started). The standalone path resolves via the GSI, reacts 👀, and spawns pr-iteration with trigger_comment_id but NO orchestration_id/iteration markers — so the reconciler ignores it. Reply (fanout dispatchToLinear): - replyToStandaloneTrigger posts the threaded ✅/❌ reply for standalone iterations only (trigger_comment_id present AND not orchestration_ iteration — orchestration iterations get the reconciler's reply). Idempotent via a conditional ack_replied_at claim (dedup redelivered terminal events). Full CDK suite: 2303 passed, 129 suites (+32 UX.3 tests).
…ain (aws-samples#247 UX.4) A sub-issue added to an in-flight epic must inherit the epic's accumulated unmerged work, not branch off bare main (user-confirmed: stack on the tip / predecessor; fall back to main ONLY when merged). - New pure module orchestration-epic-tip.ts: resolveEpicTip(existing) returns the leaf frontier (nodes nothing depends on). Integration node present → it is the single combined tip (stack on it alone, no redundant diamond). Else all real leaves: one → linear stack, many → diamond. Deterministic. - extendOrchestration injects the tip as a synthetic depends_on for new nodes that declare NO dependency (explicit edges preserved — user intent wins). This flows through the existing A4 gating + base-branch stacking unchanged. "Fall back to main only when merged" needs no new code: the agent's runtime base-fetch fallback (repo.py) already branches off default when the tip branch is gone (merged). - Concurrency (#9): a node stacked on an in-flight tip is 'blocked' and released by the reconciler from a FRESH snapshot when the tip finishes (existing machinery). FIXED: extendOrchestration now REMOVEs rollup_posted_at so adding a node to an ALREADY-COMPLETED epic lets the reconciler re-claim + re-settle the parent state when the new node lands (else the epic stuck 'in progress' forever). Tests: orchestration-epic-tip (7), extend +5 (unconstrained→tip linear/blocked/diamond, explicit-edge-preserved, rollup-claim-reset). Full CDK suite: 2315 passed.
…agent-log+CloudWatch (aws-samples#247 UX.5) The threaded ❌ reply (UX.3) now carries a real, sanitized reason and invites a retry, with two distinct shapes: - BUILD/TEST failure (agent completed, PR exists, checks red): a one-line 'build/tests didn't pass — see the PR's checks' with NO raw output dump (untrusted repo code; per-test detail isn't persisted platform-side — the PR's checks tab is the authoritative surface). - AGENT-ITSELF failure (crash / cap / timeout): the classified one-line title + a truncated 200-char excerpt + 'see CloudWatch for task <id>'. Both end 'Reply with guidance and I'll try again' — the failure reply is answerable, so the user replies @bgagent <guidance> and the comment trigger re-runs the iteration on the same PR. - New pure module failure-reply.ts: renderFailureReply. Build-vs-agent discriminated by (COMPLETED && build_passed===false && no error_message). - Wired into both UX.3 reply seams: reconciler replyToIterationComment (TerminalTaskEvent gains errorMessage, parsed from img.error_message) and fanout replyToStandaloneTrigger. - Consistency fix: fanout success = completed AND build_passed!==false, so a completed-but-build-red task gets the build-fail reply (matching the reconciler's success gate). Tests: failure-reply (9); reconciler + fanout split FAILED into agent-crash (classified + CloudWatch) and completed-build-failed (PR checks) cases. Full CDK suite: 2325 passed.
…ws-samples#247 UX.6 live-caught) Live verification on ABCA-291 surfaced a contract bug the mocked unit tests masked: Linear's commentCreate REQUIRES issueId even for a THREADED reply — parentId alone fails argument validation ('Exactly one of …issueId must be defined', INVALID_INPUT). The 👀 reaction landed fine, but the threaded ✅/❌ ack reply silently failed every time (replyToComment only logs on failure → invisible). - replyToComment(ctx, issueId, parentCommentId, body): mutation now commentCreate(input:{issueId, parentId, body}). Verified against the real Linear API (reply threads correctly under the parent comment). - Callers updated: reconciler passes the sub-issue id (changedSubIssueId); fanout passes the gated linear_issue_id. - Regression guard at the HELPER layer (per feedback_test_mock_layer): a test asserts the mutation string contains 'issueId: $issueId' inside commentCreate(input:) — a mock can't hide a missing required arg, so pin the query text. This is the 5th wiring bug from mocking too high. Full CDK suite green.
…-8hv8-536x-4wqp, GHSA-jrpj-wcv7-9fh9 (aws-samples#356) osv-scanner flagged astro 6.1.10 (docs site) for three advisories (2 High, 1 Medium); all fixed in >=6.4.6. Bump to 6.4.7 (latest 6.x), which satisfies @astrojs/starlight's astro ^6.0.0 peer. osv-scanner now reports no issues; docs site builds clean (64 pages). Co-authored-by: bgagent <345885+scottschreckengaust@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* chore(governance): granular code ownership Add maintainers overall, and then isolate adminisitrative arees. * fix: correct team name
… not a tuple the pipeline never emits (aws-samples#247 UX.6 live-caught) Forcing a deterministic build regression in live verification exposed a bug the unit tests had baked in: a build/test failure does NOT persist as COMPLETED+build_passed===false. The agent pipeline gates it to: status=FAILED, build_passed=null, error_message="Task did not succeed (agent_status='success', build_ok=False)" so isBuildFailure never matched a real regression — every build failure rendered the WRONG 'Agent task did not succeed … see CloudWatch' copy instead of 'build/tests didn't pass — see the PR's checks'. Seen live: the forced regression posted the wrong-shaped reply. - isBuildFailure now keys off the real signal: error_message matching agent_status=(success|end_turn) … build_ok=False ⇒ build/test failure (agent finished, only the gate failed). Genuine crashes (agent_status=error_*) keep the CloudWatch pointer. Defensive build_passed===false path retained. - 10 unit tests rewritten to the REAL persisted tuple — the old fixtures asserted a shape the pipeline never produces (mock-too-high in the fixtures, agreeing with the code but not reality). Full CDK suite green.
…s-samples#357) (aws-samples#359) The CDK Jest suite (//cdk:test) is ~91% of the CI build step (~649s of ~710s on the 4-core runner). ts-jest type-checks every file during the test transform, duplicating the authoritative type-check already done by //cdk:compile (tsc --build) in the same build DAG. Switch both cdk/ and cli/ Jest transforms to a dedicated tsconfig.jest.json that sets isolatedModules:true (transpile-only, no type-check). cdk's jest tsconfig also pins module:CommonJS so dynamic import() downlevels to require() (NodeNext would leave native import(), breaking jest's vm without --experimental-vm-modules). Type-safety is unchanged: //cdk:compile and //cli:compile remain the type-check gate. Local timings (8-core): //cdk:test 314s -> 155s (~2x) //cli:test 104s -> 4s (type-check was nearly the entire CLI cost) All 2041 CDK + 355 CLI tests pass; coverage thresholds unchanged and met. This is an alternative to the @swc/jest approach explored on aws-samples#357: same speedup, zero test-file changes (no ES import-hoisting fallout). Co-authored-by: bgagent <345885+scottschreckengaust@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…ples#247 UX.6 stress-caught) Live stress test surfaced a cosmetic bug: when a cascade's changed predecessor was the INTEGRATION node, the panel reason read clumsily — 'updating to include Integration — combine sub-issue results's change' (the raw synthetic title in a possessive). Other rows already relabel the integration node friendly, but the cascade REASON string did not. - New exported cascadeNodeLabel(subIssueId, identifier, title): the synthetic integration node → 'the integration' (reads 'the integration's change'); real nodes prefer the Linear identifier. - Reconciler's changedLabel now uses it. - 4 unit tests pin the integration-possessive + identifier/title/fallback. Full CDK suite green.
…h+settle the panel (aws-samples#247 UX.15 stress-caught) Live stress test left the discount epic stuck at '🔄 4/5' forever: a re-stack of ABCA-296 COMPLETED but the panel never cleared its '🔄 updating' row and the epic never re-settled to ✅. Root cause: a re-stack/iteration completing carries cascadeSubIssueId, so the handler routes it to cascadeRestack — NOT reconcileTerminalChild (which owns the panel refresh + all-terminal completion settle). When the node has no dependents (planDirectRestack=0), cascadeRestack returned EARLY, so the source node's updating row never cleared and completion never re-ran. The old comment claiming restack completions 're-fire reconcileTerminalChild' was simply false. - Extracted refreshPanelAndSettle(orchestrationId, children, meta, now), shared by reconcileTerminalChild and cascadeRestack. - cascadeRestack's no-dependents early-return now re-loads the snapshot and calls it, so the node's updating row clears and the epic settles to ✅ (or⚠️ ) + mirrors parent state. cascadeRestack got a local now. - UX.15 regression test: a re-stack of a no-dependents leaf where all children are terminal → panel refreshed ('complete', no stale 'updating') + parent state mirrored. Also includes the cascade-label fix (#47, aae0e66 already committed). Full CDK suite green.
…#288) (aws-samples#302) * docs(guides): note supported Node version range in Quick Start prerequisites * feat(types): add 'jira' to ChannelSource union Phase 1 of Jira Cloud integration (aws-samples#288). Extends the ChannelSource discriminant on both sides of the wire and updates the agent-side comment so the runtime knows 'jira' is a recognized channel value; no behavior changes yet. * feat(jira): add DDB table constructs for projects, users, workspaces Phase 2 of Jira Cloud integration (aws-samples#288). Mirrors the Linear constructs file-for-file. Composite PKs use cloudId as the tenant prefix (`{cloudId}#{projectKey}`, `{cloudId}#{accountId}`) so the same project key or account id stays unambiguous across distinct Atlassian tenants. Tables are unwired until Phase 4 — JiraIntegration instantiates and grants them. * feat(jira): add webhook, processor, link Lambdas + shared helpers Phase 3 of Jira Cloud integration (aws-samples#288). Mirrors Linear's adapter shape: per-tenant OAuth resolver (auth.atlassian.com), X-Hub-Signature HMAC verify with per-tenant + stack-wide fallback, REST-based feedback poster (ADF-wrapped, no reaction primitive — marker folded into text), and three Lambdas (webhook, processor, link). Non-trivial bit: the processor diffs `changelog.items[]` where `field === 'labels'` and tokenizes the space-separated `fromString` / `toString` to detect a label add — Atlassian's diff format differs from Linear's `updatedFrom.labelIds`. Includes a minimal ADF→markdown walker for issue descriptions. Handlers reference JIRA_* env vars set by the JiraIntegration construct in Phase 4; they don't deploy yet. * feat(jira): add JiraIntegration construct + stack wiring Phase 4 of Jira Cloud integration (aws-samples#288). Mirrors LinearIntegration: 3 DDB tables, dedup table (8h TTL), 3 Lambdas (webhook/processor/link), API routes under /jira/*, per-tenant `bgagent-jira-oauth-*` IAM grants, cdk-nag suppressions. Stack wiring grants the agent runtime GetSecretValue on the per-tenant prefix and pipes the workspace registry table + Get/Put grant into the orchestrator (matches Linear's path for pre-container failure feedback). Synth confirms clean CloudFormation + no nag findings. * feat(jira): wire agent-side MCP + OAuth resolver for jira channel Phase 5 of Jira Cloud integration (aws-samples#288). Refactors channel_mcp.py from a single-channel gate to a CHANNEL_MCP_BUILDERS dispatch dict so adding future channels stays one-entry. Adds resolve_jira_oauth_token() to config.py mirroring the Linear resolver — same race-handling, same fail-closed semantics; only differences are the endpoint (auth.atlassian.com, JSON body) and the env-var name (JIRA_API_TOKEN). Pipeline now dispatches to the right resolver based on channel_source. JIRA_MCP_URL is flagged in-source as needs-verification — Atlassian's Remote MCP may still be preview-gated; if so, fall back to a REST shim in a future jira_reactions.py module (Plan B). Tests: 6 new Jira test cases in test_channel_mcp.py; full agent suite remains green (825 passed). * feat(cli): add bgagent jira commands (app-template, setup, link, map) Phase 6 of Jira Cloud integration (aws-samples#288). Minimal v1 surface (4 of 10 Linear subcommands), per scoping decision. Mirrors the Linear CLI shape where the contracts are similar: - jira-oauth.ts ports linear-oauth.ts. Atlassian's token endpoint takes JSON (Linear takes form-encoded). offline_access scope is required for a refresh_token. fetchAccessibleResources() resolves cloudId + siteUrl post-consent. - commands/jira.ts: app-template prints dev-console values; setup drives the OAuth dance + writes the per-tenant secret + registry row + webhook signing secret; link does dry-run preview UX; map writes the project → repo row. Deferred to follow-ups: add-workspace, update-webhook-secret, invite-user (with self-link picker), list-projects. * test(jira): add webhook, processor, and link handler tests Covers signature verify pass/fail, dedup, event filtering, label-add detection (create vs update changelog), and Cognito-authenticated linking. 56 tests, mirrors the Linear handler test surface. * docs(jira): add setup guide, ADR-014, and integration listings - docs/guides/JIRA_SETUP_GUIDE.md — OAuth 3LO app, scopes, webhook registration, label trigger, project mapping, troubleshooting - docs/decisions/ADR-014-jira-integration.md — Jira Cloud only, OAuth 3LO, label trigger, MCP outbound; documents the Jira-vs-Linear divergences - README, USER_GUIDE, ROADMAP — add Jira to channel listings - sync-starlight.mjs + astro.config.mjs — register the Jira guide mirror; regenerate Starlight content under docs/src/content/docs/ Completes the docs phase of aws-samples#288. * test(jira): close build/coverage gaps for jira integration Bring `mise run build` green on the jira integration branch: - check-types-sync: allowlist JiraLinkResponse as CLI-only, matching SlackLinkResponse/LinearLinkResponse (link responses are inlined server-side; no CDK source-of-truth type) - channel_mcp.py: move Callable into a TYPE_CHECKING block (ruff TC003; safe under `from __future__ import annotations`) - agent.test.ts: bump expected DynamoDB table count 13 -> 17 for the four new Jira tables (project/user/workspace-registry/webhook-dedup) - test_config.py: cover resolve_jira_oauth_token (cache, fallback, refresh, concurrent-refresh, malformed/expiry paths); agent coverage 70.41% -> 72.91% - jira-oauth-resolver.test.ts: new suite (32 tests) mirroring the Linear resolver tests; clears the CDK statement/line/function/branch gates - jira.ts / jira-oauth.ts: ESLint --fix cosmetic edits (quote-props, redundant template literals) Tests: 294 CLI + 837 agent + 1896 CDK, all passing. * fix(jira): resolve cloudId from sole tenant when webhook omits it Jira webhooks created via the Settings → System → Webhooks UI do not include a top-level `cloudId` in their payload (only app/OAuth-registered dynamic webhooks do). Without it the processor can't resolve the tenant, so it dropped the event and never created a task — the inbound trigger silently failed for the common single-tenant, UI-webhook setup. Add a safe fallback: when `payload.cloudId` is absent, scan the workspace registry and use the sole `active` tenant. Deliberately refuses to guess when zero or multiple active tenants exist (returns undefined → event dropped), so the multi-tenant design is preserved — a multi-tenant operator must use a webhook that carries its own cloudId. `grantReadData` on the registry table already covers the Scan, so no IAM change is needed. Adds tests for: sole-tenant recovery (task created), empty registry (drop), and multiple active tenants (ambiguous → drop). * feat(jira): post issue progress comments via REST shim Jira-origin tasks now comment on the originating issue at start ("🤖 picked up…") and on completion ("✅ finished — PR: <url>" / "❌ …"), matching the Linear integration's progress UX. Why a REST shim instead of the Atlassian Remote MCP: the hosted MCP (mcp.atlassian.com) requires an interactive, browser-based OAuth 2.1 flow with dynamic client registration — it does NOT accept the stored Jira REST OAuth token as a Bearer header, so it fails to connect from a headless agent ("claude mcp list" → Failed to connect; no mcp__jira-server__* tools load). The Jira REST API accepts the same stored token (it carries write:jira-work), so comments go via POST /rest/api/3/issue/{key}/comment on the cross-region api.atlassian.com/ex/jira/{cloudId} base. - New `jira_reactions.py`: gated by channel_source=='jira' + required metadata; swallows all network/auth errors (comments are advisory, never gate the pipeline); auth circuit-breaker mirrors linear_reactions. - Wired into pipeline.py at task start, normal finish (with PR url), and the crash path — parallel to the existing Linear reaction hooks. - prompt_builder: Jira tasks now get NO MCP-comment addendum (the earlier Linear-only gate already skipped them); instructing the agent to use the non-loading MCP tools would just waste turns. Comments are out-of-band. Adds test_jira_reactions.py (gate, ADF body, success/failure/PR variants, error-swallowing, auth circuit breaker) and channel-addendum tests. * fix(jira): repair botched merge in test_config.py imports The 'Merge branch main' commit (c84cc66) left an invalid import block: a missing comma after PR_WORKFLOW_IDS (syntax error) plus a stale PR_TASK_TYPES import that main's aws-samples#248 removed from config.py. ruff rejected the file, aborting the agentcore build. Drop the orphaned PR_TASK_TYPES import and fix the comma. * fix(jira): type _config base dict so ty accepts TaskConfig(**base) test_prompts.py:_config built base from a homogeneous str literal, so ty inferred dict[str, str] and rejected the spread into TaskConfig's bool/int/list fields (17 invalid-argument-type errors). Annotate base as dict[str, Any], matching the existing helper in test_runner.py. This failure was previously masked by the lint syntax error that aborted the build before typecheck ran. * fix(jira): apply ruff format and resync stale docs mirrors Three more issues that were masked behind the earlier lint/typecheck failures, all surfaced once the build progressed to its 'fail on mutation' gate: - ruff format reflowed two long boolean/string lines in jira_reactions.py and test_jira_reactions.py that were committed unformatted. - USER_GUIDE.md still referenced the retired `pr_review` task_type on the intro 'For example' line (a aws-samples#248 merge leftover); the rest of the guide uses `coding/pr-review-v1`. Fixed the source and regenerated the Starlight mirror (using/Overview.md). - Quick-start mirror was missing the Node.js prerequisite line present in the QUICK_START.md source; docs-sync adds it. Full `mise run build` now completes with no working-tree mutation. * fix(jira): address PR aws-samples#302 review — security binding, token refresh, ADR/docs Blocking (krokoko): - Multi-tenant signature binding: webhook receiver flags stack-wide verification to the processor, which then ignores the body cloudId and binds to the sole active tenant (drops when ambiguous). CLI no longer mirrors the stack-wide secret into new per-tenant bundles; stack-wide is seeded once from the first tenant. Missing-timestamp replay skip is logged. - Renumber ADR-014 -> ADR-015 (collides with workflow-driven-tasks); rewrite to the implemented REST-outbound reality (status accepted), correct dedup key, binding + refresh-ownership sections. Reconcile JIRA_SETUP_GUIDE, USER_GUIDE ("six ways"), ROADMAP, channel_mcp.py (placeholder + in-band log), jira-webhook-processor.ts, jira-integration.ts, agent.ts. - Fix non-existent CLI command in feedback: onboard-project -> map <cloud-id> <project-key> --repo (processor + CLI next-steps hint). - Implement notifyJiraOnConcurrencyCap (Linear parity) so the orchestrator IAM grant is used and Jira users aren't silently dropped on the cap. Significant (ayushtr): - Agent never refreshes the Jira token (Atlassian rotates refresh_tokens; agent has GetSecretValue only). Use the Lambda-written token verbatim and fail closed when expiring; Lambda path owns all refreshes. - ADF media nodes (external images) now render to markdown so attachment extraction works; ADF->markdown computed once and reused. Minor: one-sided clock-skew-tolerant timestamp freshness, base64-body guard, replay window 24h->1h, resolveSoleTenantCloudId Scan comment. Tests: agent no-refresh/fail-closed suite, multi-tenant binding tests, base64/missing-timestamp/stack-wide-flag webhook tests, ADF media-node test, notifyJiraOnConcurrencyCap parity suite. Docs mirrors regenerated. Relates to aws-samples#288 * fix(docs): repair botched main-merge in sync-starlight.mjs The 'Merge branch main' commit (0f47343) dropped the closing ');' on the Jira mirrorMarkdownFile() call where it interleaved with main's new 'Deploy preview screenshots' mirror block, producing a SyntaxError that broke the //docs:sync build step (and thus the whole build job). * fix(cdk): repair botched main-merge in agent.ts The 'Merge branch main' (0f47343) dropped the closing '});' on the JiraWorkspaceRegistryTableName CfnOutput where main's new GitHubScreenshotIntegration block was spliced in, cascading into ~40 TS1005 errors and breaking //cdk:compile. * fix(cdk): correct DynamoDB table-count assertion + import ordering Collapse the duplicated 17/14 table-count assertions left by an earlier botched main-merge into a single correct count of 18 (17 enumerated tables incl. the 4 Jira tables, plus github-webhook-dedup from GitHubScreenshotIntegration). Also fix import ordering (github before jira) flagged by eslint import/order. * fix(jira): post Lambda-side feedback to api.atlassian.com gateway base The Lambda-side Jira feedback helper built its REST URL from the tenant site host (`*.atlassian.net`), but the per-tenant 3LO token is minted with `audience=api.atlassian.com` and is only valid against the gateway base `https://api.atlassian.com/ex/jira/{cloudId}/rest/...`. Every pre-container feedback comment (unmapped project, unlinked user, concurrency-cap rejection, createTaskCore non-201) therefore 401'd silently, since these comments are best-effort and swallow errors. Build the URL from `cloudId` (already on `JiraFeedbackContext`) against the gateway base — matching the agent-side path in jira_reactions.py — and drop the unused `siteUrl`. Correct the misleading jira-workspace-registry-table comment that called site_url a "REST base". Adds jira-feedback.test.ts asserting the request host is api.atlassian.com (never atlassian.net), plus encoding and never-throws coverage. * fix(jira): extract magic numbers into named constants Resolves @typescript-eslint/no-magic-numbers eslint errors that were failing the cdk:eslint build step. Mirrors the named-constant convention already used by the Linear integration siblings. * fix(jira): address review — HMAC empty-secret guard, verify tests, doc fixes Resolves the blocking items and nits from the PR aws-samples#302 review. Blocking: - B1: route the Jira webhook signing secret through isUsableHmacSecret in both getJiraSecret (on fetch) and verifyJiraSignature (defense-in-depth), matching the Linear/Slack/GitHub invariant. A whitespace-only per-tenant secret was previously truthy and made HMAC(' ', body) forgeable. - B2: add cdk/test/handlers/shared/jira-verify.test.ts (30 cases) covering verifyJiraRequestForTenant's four outcomes (verified/mismatch/revoked/ no-per-tenant-secret), the strict-lookup rethrow, verifyJiraRequest rotation-refetch, empty/whitespace-secret rejection, and the one-sided timestamp freshness window (stale / far-future / within-skew). Mirrors linear-verify.test.ts; the multi-tenant trust boundary had no coverage. - B3: rewrite the JiraWorkspaceRegistryTable docstring (and the matching jira-integration.ts comments) to describe the real oauth_secret_arn / Secrets Manager resolution path instead of the never-implemented provider_name / AgentCore Identity model. Nits: - Reword the stale "MCP token" comment in jira-webhook-processor.ts to the REST-outbound reality. - Add ts-/py-silent-success-masking nosemgrep justifications to the best-effort swallows in jira-verify.ts, jira-oauth-resolver.ts (3), and jira_reactions.py, matching the Linear annotations. - On a refresh PutSecretValue failure, no longer cache the rotated token (SM holds a stale refresh_token); invalidate the cache and escalate the log so the breakage surfaces promptly. - URL-encode the cloudId path segment in jira-feedback.ts and both cloud_id and issue_key in jira_reactions.py (defense-in-depth; issueKey was already encoded on the TS side). - Re-extract the inlined 512 into WEBHOOK_PROCESSOR_MEMORY_MB (AI007). mise run build green: 2177 CDK + 355 CLI + 1090 agent tests passing. Relates to aws-samples#288 --------- Co-authored-by: bgagent <bgagent@noreply.github.com> Co-authored-by: Alain Krok <alkrok@amazon.com> Co-authored-by: Sphia Sadek <isadeks@gmail.com>
* chore(cicd): specific bot token * fix: update the token
…tate re-settles (aws-samples#247 UX.15b stress-caught) Live re-verify of the UX.15 stuck-panel fix surfaced a second facet: the epic panel BODY re-settled to ✅, but the parent REACTION stayed 👀. Cause: rollup_posted_at was stamped at the FIRST completion, so on any re-completion claimRollup fails → mirrorParentState is skipped → the Linear reaction/state never re-mirror (👀→✅). extendOrchestration already clears the claim on the extend path, but a CASCADE re-open never did. - New clearRollupClaim store op: unconditional REMOVE rollup_posted_at (idempotent). - cascadeRestack's re-open branch (a cascade that re-opens the epic with '🔄 updating' rows) clears the claim, so when the re-stacks finish the parent state re-settles 👀→✅. - Fixed the stale code comment that falsely claimed restack completions 're-fire reconcileTerminalChild'. - Tests: reconciler 'cascade re-open clears rollup_posted_at' + store clearRollupClaim unit test. Full CDK suite green.
…d ROOT (aws-samples#247 UX.11 stress-caught) A @bgagent trigger that is itself a thread-reply had its ✅/❌ ack SILENTLY dropped — the reconciler logged Linear's 'Parent comment must be a top level comment' rejection (commentCreate rejects a reply whose parentId is itself a reply; Linear threads are one level deep). The 👀 landed (reactions ignore thread depth) but the reply never posted. - LinearCommentEvent.data gains typed parentId (the thread root for a reply). - Both trigger paths now set channel_metadata.trigger_comment_id = data.parentId ?? data.id (the ROOT), while the 👀 still reacts on the actual comment the human wrote. handleStandaloneCommentTrigger gains a replyTargetId param. A top-level trigger's parentId is undefined → falls back to commentId (unchanged behavior). - Regression test: thread-reply trigger → 👀 on the reply, reply target = the root. Full CDK suite green.
…arPostResult refactor) Brings 20 upstream commits onto the integration branch (additive-diff deploy pattern), incl. the Jira Cloud integration (aws-samples#288/aws-samples#302) and the linear-feedback LinearPostResult/retryable refactor (aws-samples#311/aws-samples#332). 8 conflicts resolved: - linear-feedback.ts: kept both my aws-samples#247 comment-reaction helpers and upstream's LinearPostResult type; adapted 5 helpers (reactToComment, swapIssueReaction, transitionIssueState, upsertStatusComment, + orchestration-rollup) to the new graphqlRequest LinearPostResult contract via.ok. Fixed a latent bug the refactor surfaced: upsertStatusComment's 'ok ? id : null' would always report success (object truthy) — now reads.ok. - agent/tests/test_repo.py: took upstream's hermetic harness as base, ported my 3 A4-stacking branch-name-verbatim tests onto make_task_config (#14 regression coverage preserved). - fanout-task-events.test.ts: upstream's { ok: true } mock + kept the replyToComment mock. - agent.test.ts: DynamoDB table count → 19 (14 base + 4 Jira + 1 orchestration); verified live by the suite. - DEVELOPER_GUIDE.md / ROADMAP.md: kept both additions; regenerated the Starlight mirrors via docs sync. Compiles + lint clean; full CDK suite passes (195 touched-suite tests incl. the 19-table assertion; the only non-zero exit is the global coverage gate on partial runs).
… the parent panel (aws-samples#247 #57) User-spotted gap: the parent epic panel never showed a combined preview screenshot live. renderEpicPanel had the capability (combinedScreenshotUrl + the ![combined preview] embed) but no caller populated it — and the screenshot pipeline never PERSISTED the URL (it parsed the Linear identifier from the PR branch, posted a comment, and discarded the URL), so there was nothing for the reconciler to read. - screenshot-url.ts: new pure extractTaskIdFromBranch (taskId is the 2nd segment of bgagent/{taskId}/{slug}). - github-webhook-processor: persistScreenshotUrl writes screenshot_url onto the deploy task's TaskRecord (keyed by the branch taskId), conditional attribute_exists, best-effort, behind a new TASK_TABLE_NAME env var. TaskRecord.screenshot_url added. - reconciler: resolveCombinedScreenshotUrl(integration.child_task_id) — one Get — passed as combinedScreenshotUrl to upsertEpicPanel ONLY on the all-terminal settle when an integration node exists. - GitHubScreenshotIntegration: new taskTable prop → TASK_TABLE_NAME env + grantWriteData; wired taskTable.table in agent.ts. Tests: extractTaskIdFromBranch (4); reconciler '#57 all-terminal + integration node → embeds combined screenshot'. Full CDK suite green (133 across the touched suites). Only meaningful for fan-out epics with an integration node + a real UI deploy.
…aws-samples#247 #58 deploy-block) Deploying #57 rolled the stack back on AWS::Logs::DeliverySource 'AlreadyExists': the agentcore-alpha Runtime auto-creates DeliverySource + Delivery + DeliveryDestination per loggingConfig, and a construct-path rename across alpha builds churned BOTH the CFN logical IDs AND the account-scoped DeliverySource/DeliveryDestination Names. Because those Names are account-unique, CFN's create-before-delete on the churned ids hits AlreadyExists and rolls the whole stack back. (Not a #57 bug — any deploy was blocked.) Fix: pin the 4 account-name-unique log resources (2 DeliverySource + 2 DeliveryDestination) to their DEPLOYED logical IDs + Names via overrideLogicalId + addPropertyOverride('Name', …), so CFN sees them as the SAME resources and updates in place (diff: [~] DependsOn-only, no replacement). The 2 CfnDelivery links have no Name and Ref the pinned ids → harmless replace. Log delivery is stateless routing config. Best-effort tryFindChild so a future alpha rename silently no-ops. cdk diff confirms: Source/Dest = [~] in place (no AlreadyExists); Delivery = [-]/[+] harmless. agent.test.ts (44) green.
deploy-block, round 2) Round-1 pinning fixed the DeliverySource/DeliveryDestination collision — all 4 updated in place — but the rollback just moved one resource downstream to AWS::Logs::Delivery ('identifier null already exists'). A Delivery has no Name, but it IS unique per (source, destination) pair; with the source+dest now pinned, the churned Delivery logical id tried to create-before-delete a SECOND link over the same pair → AlreadyExists. Pin the 2 Delivery links' logical IDs too (logical-id only — no Name). pinLogResource's liveName is now optional. cdk diff: all 6 log resources [~] in place (Delivery no longer [-]/[+]); the inner [-]/[+] is just DependsOn reordering. agent.test.ts (44) green.
… + panel preview deep-links (aws-samples#247 UX.16 + UX.17) Live-caught on the ABCA-301 fan-out epic: the synthetic integration node has NO Linear sub-issue of its own, so its work spilled THREE standalone comments onto the PARENT epic — '🤖 Starting integration…', '🔗 PR opened: aws-samples#191', '🖼️ Preview screenshot' — a comment stream that violates the locked 'one maturing panel, no comment stream' spec and 100% duplicates what the panel already shows (Integration row ✅ + Combined PR + Combined preview). Separately, the panel's combined preview embedded the image but had no clickable link to the running combined deploy. UX.16 — stop the flood (two emitters): - agent: prompt_builder._channel_prompt_addendum now returns '' when channel_metadata has no linear_issue_id. The integration node is the only Linear task without one (orchestration-release omits it on purpose), so the agent no longer gets the 'post Starting/PR-opened to Linear' instructions → no groping onto the parent. Real sub-issues are unaffected (they have linear_issue_id). - screenshot pipeline: persistScreenshotUrl now returns whether the deploy task is an integration node (read from the UpdateItem ALL_NEW channel_metadata.orchestration_sub_issue_id — no extra Get), and the processor SKIPS the standalone Linear screenshot comment for it. The GitHub PR comment still posts (load-bearing on the PR); the panel embed is the only Linear surface for the combined result. UX.17 — panel preview deep-link: - persist screenshot_preview_url (the live Vercel/Netlify deploy URL the shot was captured from) alongside screenshot_url; thread it through resolveCombinedScreenshotUrl → upsertEpicPanel → renderEpicPanel. - the panel's combined preview is now a clickable linked image ([](preview)) + an 'Open the combined preview' link. Falls back to the plain image when no preview URL is known. The preview URL is payload-derived → parens percent-encoded (markdown breakout defense, same as the screenshot comment renderers). Tests: agent test_linear_integration_node_gets_no_addendum (+ existing linear test now carries linear_issue_id); processor 'persists BOTH urls' + 'integration node deploy persists but posts NO standalone Linear comment'; renderEpicPanel deep-link + paren-encode + fallback; reconciler #57 test extended to assert the deep-link. Also refreshed 8 stale postRollup mocks to the merged LinearPostResult {ok} contract (merge left them returning bare booleans → 5 pre-existing reds, now green). cdk:compile clean; agent suite 1116 green.
…to the sub-issue they name (aws-samples#247 UX.18) Live-caught on ABCA-304: a reviewer commented '@bgagent for the footer can you change it to...' on the PARENT epic (the maturing panel lives there, so that's the natural place to comment) and it was SILENTLY DROPPED. The parent epic has no PR of its own, so the comment-trigger fell through to the standalone GSI path, found no task for the parent issue, and ignored it ('issue has no ABCA task — ignoring'). Fix: in handleCommentTrigger, detect when the commented issue is itself an orchestration PARENT (deriveOrchestrationId(issueId) loads an orchestration whose meta.parent_linear_issue_id === issueId — a pure hash, so the parent's own id maps to its orchestration; a sub-issue's doesn't). Route to handleParentEpicCommentTrigger: - 👀 on the comment IMMEDIATELY — a parent comment is never silently dropped again. - parseParentNodeReference(instruction, children) picks the target sub-issue: Linear identifier (ABCA-305) wins outright, else a significant (non-noise) title keyword ('footer' → 'Add a site-wide footer'). Exactly one started node with a PR → iterate it via the shared iterateOrchestrationChild (same pr-iteration + cascade marker + threaded ✅/❌ reply as commenting on the sub-issue directly). - 0/ambiguous match, or matched node has no PR → post a threaded reply on the parent: a best-effort 'did you mean <X>?' suggestion, the list of sub-issues + how to target one (@bgagent ABCA-123:...), and the 'create a sub-issue for NEW work' path. NEVER auto-creates an issue and NEVER silently drops (user's call: ask, don't create). Refactor: extracted the per-child iteration into iterateOrchestrationChild (skipAck/prNumber params) so the direct sub-issue path and the new parent path share one code path. New pure module orchestration-parent-comment.ts (parseParentNodeReference + suggestClosestNode + renderParentDisambiguationReply), 16 unit tests incl. the exact live case. 4 handler-wiring tests (live case, identifier targeting, ambiguous→ask, no-match→ask). Existing A6 sub-issue + standalone trigger tests unchanged + green (36→40 in the orch suite). cdk:compile clean.
…et (aws-samples#368) (aws-samples#372) Admin-UI Jira webhooks omit cloudId, so the receiver verifies against the stack-wide secret. But `bgagent jira setup` only seeds that secret when it looks unconfigured, and its heuristic (`!startsWith('{')`) misread CDK's bare-string auto-generated value as operator-set — so the pasted secret was never applied and every delivery failed HMAC with a silent 401. - CDK: give the WebhookSecret an explicit JSON placeholder via generateSecretString, carrying a marker key, so the value genuinely starts with '{' as the CLI assumed. - CLI: recognize the placeholder by its marker key (parse + key check) instead of the fragile startsWith('{') shape heuristic; a malformed or markerless value is treated as operator-set. - Add CLI + CDK construct tests covering the placeholder cases, plus coverage for adjacent jira.ts/linear.ts helpers pulled into the coverage denominator by the new test imports. - CI: use gitleaks-safe test fixture strings and cover openBrowser platform branches so branch coverage stays above threshold on Linux. Migration: stacks deployed before this change keep their bare-string placeholder (CFN regenerates secret values only on create), so they must redeploy before setup will seed; documented in code + PR. Co-authored-by: bgagent <bgagent@noreply.github.com> Co-authored-by: Cursor <cursoragent@cursor.com>
…-routed iteration's ✅/❌ reply lands (aws-samples#247 UX.19) Live-caught on ABCA-304 immediately after UX.18 shipped: a comment left on the PARENT epic was routed to the footer sub-issue and an iteration spawned (👀 ack + 'routed to sub-issue … pr_number 193'), but the iteration then FAILED (transient GITHUB_UNREACHABLE) and NO ❌ reply appeared — the human saw 👀 then silence. Root cause: replyToIterationComment posted the ✅/❌ reply with issueId = changedSubIssueId (the sub-issue, ABCA-305), but the trigger comment lives on the PARENT epic (ABCA-304). Linear's commentCreate rejects a threaded reply whose parentId belongs to a different issue, so the reply silently failed. (The cascade-skip on FAILED was correct; the reply, which runs BEFORE the success gate, was the casualty.) Fix: thread trigger_comment_issue_id (the issue the human actually commented on) through channel_metadata — - iterateOrchestrationChild adds it (defaults to the sub-issue id; the parent-epic path passes snapshot.meta.parent_linear_issue_id). - parseTerminalTaskRecord reads it into TerminalTaskEvent. - replyToIterationComment uses it as commentCreate's issueId, falling back to changedSubIssueId for pre-UX.19 tasks. Direct sub-issue comments are unaffected (issue id == sub-issue). The standalone fanout reply path already replies on linear_issue_id = the comment's own issue, so no change needed there. Tests: reconciler 'PARENT-routed iteration replies on the PARENT issue not the sub-issue'; webhook wiring asserts trigger_comment_issue_id = the parent. 78 green across reconciler+webhook+matcher suites.
…stop webhook-redelivery reply spam (aws-samples#247 UX.20) CRITICAL live-caught: a no-match @bgagent comment on the parent epic spammed 50+ duplicate disambiguation replies. Root cause: the UX.18 parent-comment handler posts its reply (and 👀) with NO idempotency guard, and Linear REDELIVERS a comment webhook whenever the handler exceeds its ~5s ack window (this path does several Linear API calls and ran 7.7s). Each redelivery re-ran the whole handler → another reply. The iteration path is deduped by ack_replied_at; the disambiguation path I added in UX.18 had nothing. (Mitigated live by throttling the Linear receiver+processor Lambdas to 0 concurrency; 49 spam comments deleted.) Fix: new store op claimCommentAck — a conditional create-once write keyed on (orchestration_id, 'ack#<commentId>') with a TTL, mirroring claimRollup. handleParentEpicCommentTrigger claims BEFORE any side-effect; only the first delivery proceeds (👀 + match/route/reply), redeliveries no-op. The marker self-expires via the table's existing ttl attribute. loadOrchestration now excludes any 'ack#…' (and any '<kind>#' marker) SK from the children list so the dedup rows can't be mistaken for sub-issues (real child SKs are UUIDs or '…__integration', never contain '#'). Tests: claimCommentAck (first-wins / redelivery-loses / error-propagates); loadOrchestration excludes ack# rows; webhook 'redelivery posts EXACTLY ONE reply' + 'matched iteration dedups to one task'. 58 green across store+webhook suites.
…he self-reply loop (aws-samples#247 UX.20 root cause) The 50-reply spam was NOT webhook redelivery (my first UX.20 theory) — it was a genuine INFINITE SELF-TRIGGER LOOP: the parent-epic disambiguation reply embeds a literal example '@bgagent ABCA-123: <what to change>'. That reply is itself a Linear comment → fires a Comment webhook → parseCommentTrigger's /@bgagent/ regex matched the mention INSIDE the bot's own reply → posted another disambiguation reply (also containing @bgagent) → … ~50 deep. Each iteration was a NEW comment with a NEW id, so the per-comment ack-claim (UX.20 round 1) couldn't dedup it — it guarded redelivery, not self-reference. Root fix (the guard the Linear path never had — Slack already skips its own bot_id messages to avoid exactly this): parseCommentTrigger now returns NOT-triggered for any comment whose trimmed body starts with one of the bot's own template markers (👋 ✅ ❌⚠️ 🔄 🤖 🖼️ 🔗) — exported isBotAuthoredComment. The bot can never act on a comment it authored, regardless of what example text the body contains. The pre-existing A6 trigger never looped only because the agent's progress comments happen not to contain @bgagent; UX.18's reply was the first bot comment that did. The UX.20-round-1 ack-claim (claimCommentAck) stays — it's still correct defense against genuine webhook redelivery; this is the orthogonal, primary fix for self-reference. Tests: the EXACT spam body (👋 + embedded @bgagent example) → not triggered; every template prefix → bot-authored; a real human @bgagent → still triggers; leading-whitespace marker still caught. 41 green across trigger+webhook suites.
…ation done — 👀→✅/❌ + In Review (aws-samples#247 UX.21) User-caught, three symptoms one cause: after a comment-iteration finishes, (1) the trigger comment stayed on 👀 forever (never ✅), (2) the sub-issue state flapped In Progress/In Review, and (3) the panel showed ✅ (correct, reads child_status) while the sub-issue said In Progress — three views disagreeing. Root cause: the reconciler posted the threaded ✅/❌ reply but never settled the comment REACTION or the sub-issue STATE; state was delegated to the AGENT via a prompt instruction (non-deterministic, raced, got stuck). Fix: the platform now owns settlement (as it already does for the parent epic's reaction/state). In replyToIterationComment, after the reply: - swap the TRIGGER comment 👀 → ✅ (success) / ❌ (failure) so the comment reads done at a glance, not just the threaded reply. - on success, advance the SUB-ISSUE to In Review (PR updated & open, awaiting human merge — same convention the epic uses; user's choice). On failure, leave the state (never demote). The reaction/state target the actual trigger comment + the iterated sub-issue, so a parent-routed comment settles the parent's comment + the footer sub-issue correctly. Gated once-only by the existing ack_replied_at claim; both helpers are idempotent (re-converge to one marker / skip if already in target state). New helper swapCommentReaction (mirrors swapIssueReaction but on a COMMENT — queries comment(id){reactions}, deletes stale bgagent markers, adds the target; never touches a human reaction). Tests: helper (swap/idempotent/ human-safe/no-token) + reconciler success→✅+In Review, failure→❌+no transition. 80 green across linear-feedback + reconciler suites.
…d migration shim (no more hardcoded-into-every-stack) User flagged the hardcode: the #58 fix pinned backgroundagent-dev's live CloudFormation logical IDs + account-unique Names directly into agent.ts, applied UNCONDITIONALLY. The old comment falsely claimed a fresh stack "just deploys clean" via tryFindChild no-op, but the children DO exist on every stack, so it would force ANY stack (a fresh deploy, another account, CI, this code on main) to adopt the dev stack's identities. Wrong. Why the pins exist at all: only a stack deployed BEFORE the agentcore-alpha bump has pre-existing churned resources to collide with (AlreadyExists on create-before-delete). A fresh stack has nothing to collide with and MUST synth the current alpha's natural ids. Fix: PINNED_LOG_DELIVERY_BY_STACK keyed by stackName (only "backgroundagent-dev" listed) plus maybePinChurnedLogResources that applies the overrides ONLY when the running stackName matches (or a "-c pinnedLogDeliveryStack=name" context selects it). Every other stack gets no overrides and a pristine synth. Verified: real backgroundagent-dev synth still pins (deploy stays unblocked); a different stack name synths natural alpha ids. Delete the table entry + helper once the dev stack is migrated. agent.test.ts (44) green.
… in gitleaks CI secret-scan (gitleaks generic-api-key) false-positived on the 'orch_abc_SUB-1'-style idempotencyKey fixtures in the orchestration tests (orchestration-release.test.ts / orchestration-reconciler.test.ts) — these are made-up test values, not credentials. Scope a targetRules+paths allowlist to those two test files, mirroring the existing wat-opaque-123 / test-signing-secret-abc123 fixture exemptions. Range scan: no leaks found.
…ject.toml (aws-samples#532) (aws-samples#539) * fix(security): suppress uv-missing-dependency-cooldown on agent/pyproject.toml (aws-samples#532) semgrep's uv-missing-dependency-cooldown rule wants a global exclude-newer cooldown under [tool.uv]. Setting one is structurally incompatible with this repo: deps are exact-pinned (==) and several pins (fastapi, bedrock-agentcore, boto3) are to versions published within any reasonable cooldown window, so a global exclude-newer makes `uv lock`/`uv sync` unsatisfiable and re-breaks on every fresh pin bump. The rule's threat model (surprise malicious/unstable version) is already covered by exact pins + committed uv.lock + reviewed upgrade-main PRs + uv sync --frozen in the image. Suppress with a justified nosemgrep on the line preceding [tool.uv] rather than adding a redundant, workflow-breaking cooldown. Approach approved by maintainer (Option B) in aws-samples#532. Verified: `mise run security:sast` -> 0 findings; `uv sync --all-groups` and `uv lock --check` both resolve. Fixes aws-samples#532 Co-Authored-By: Claude <noreply@anthropic.com> * chore: re-trigger CI — clear stuck CodeQL run (aws-samples#532) --------- Co-authored-by: Mandeep Singh <mandeep408@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Alain Krok <alkrok@amazon.com> Co-authored-by: Sphia Sadek <isadeks@gmail.com>
…ot tens of GB) Corrects an earlier over-cautious estimate. cdk:test at 4 workers peaks at only ~2.2 GB across the whole process tree (sampled on a 16 GB Mac, no swap). The ABCA-685 OOM was NOT cdk:test's worker count — it was TOTAL build concurrency (full-parallel mise: cdk:test + agent:test + cli + docs + synth + the resident agent at once). So 4 jest workers is comfortably safe on the 120 GB box; the real memory driver is cross-package parallelism, not jest's internal fleet. Kept as an explicit env so a bigger box can't silently over-spawn; local/CI keep 25%. Will live-verify the ECS MemoryUtilized curve on the next fork run.
…r (kills the --no-verify OOM) The target repo's `mise run install` installs a prek pre-push hook that re-runs the FULL cdk+cli+agent suite on every git push. In the agent container that suite already ran twice (baseline + post-agent build gate) and CI runs it again, so the pre-push run is pure redundancy — and it runs UNcapped (no JEST_MAX_WORKERS), stacking on the resident agent → OOM. The agent's only escape was `git push --no-verify`, which bypassed ALL hooks (incl. the security scan) and trained a skip-verification habit (the honesty gap: agent reported build_passed while its push had skipped verification). Set SKIP=monorepo-tests-pre-push (the pre-commit/prek standard env) on the build task def — scoped to ONLY the tests hook, so pushes succeed without --no-verify while the pre-push SECURITY scan still runs. Propagates to platform + agent pushes via shell.py::_clean_env (blacklist). Deploys alongside JEST_MAX_WORKERS=4.
* feat(cli): add jira invite-user
* refactor(cli): address PR review for jira invite-user
- Extract generateInviteCode + INVITE_CODE_ALPHABET into shared
cli/src/invite-code.ts; import from both jira.ts and linear.ts
(drops the require('crypto') + eslint-disable in the Jira copy).
- Move parseStoredJiraOauthToken to jira-oauth.ts alongside the other
OAuth helpers; jira.ts stays focused on command wiring.
- Warn (non-fatal) in `jira invite-user` when the resolved Jira identity
is already actively linked, so the admin knows the code will re-link.
- Document `bgagent jira setup/map/invite-user/link` in cli/README.md.
- Add tests: already-linked warning, inactive/app account rejection,
malformed OAuth secret JSON, missing oauth_secret_arn, and missing
stack outputs — closing the branches Codecov flagged.
* refactor(cli): address second-pass PR review nits for jira invite-user
theagenticguy's review (against pre-refactor 08a5d81) raised two nits not
covered by the earlier fixes:
- Remove modulo bias in generateInviteCode via rejection sampling: bytes
>= the largest alphabet multiple under 256 are discarded so the char
distribution is uniform (previously indices 0-7 were favored 9/256).
- Guard the pending-row PutCommand with
ConditionExpression: attribute_not_exists(jira_identity) so a code
collision fails loudly with an actionable error instead of silently
overwriting a still-valid invite.
Tests: new cli/test/invite-code.test.ts (100% cover incl. rejection +
top-up branches and a distribution smoke check); jira.test.ts gains the
conditional-put assertion and a ConditionalCheckFailedException case.
The review's third nit (inline require('crypto') + eslint-disable) was
already resolved by the prior extraction to cli/src/invite-code.ts.
* chore: re-trigger CI
…CS test-hang The agent build gate (`mise run build` in the cloned repo) has intermittently wedged on the ECS substrate with the test suite hanging silently for ~53 min, up to the platform's 3600s build-verify ceiling (ABCA-684/686/688 + the warm-cache run). pytest-timeout with method="signal" did NOT fire: SIGALRM only interrupts the MAIN thread during a test's call phase, so a hang in a worker thread (a deadlocked Barrier/join), a fixture, collection, or a C-level socket read is invisible to it. Add the instrument that IS immune to all of those: - pyproject.toml: faulthandler_timeout=300 — per-test all-thread stack dump. - tests/conftest.py: a session-level faulthandler.dump_traceback_later(1200, exit=True) watchdog on a dedicated C thread (immune to the GIL and blocked syscalls) that dumps every thread and HARD-EXITS at 20 min, well inside the 3600s ceiling. The next hang self-reports its exact file:line instead of stalling blind. Also close a real cross-test leak that is a candidate root cause of the hang: aws_session caches the resolved boto3 session in a module global, and tenant_client returns session.client(...) from that cache when scoped — bypassing a downstream @patch("boto3.client"). Only test_aws_session reset the cache; a scoped session left cached by any other test could make a later test (e.g. test_attachments) issue a REAL S3 call that hangs on the ECS network. The autouse _clean_env fixture now calls reset_session_cache() so every test resolves the session under its own patches. Full agent suite (1315) green; mise run quality green (coverage 80.90%).
…ity) The ECS build task role gets MEMORY_ID in its container env (so the agent attempts write_task_episode / write_repo_learnings) but was never granted grantReadWrite on the AgentMemory. The writes use the task role's ambient credentials, so they failed closed with: AccessDeniedException … not authorized to perform: bedrock-agentcore:CreateEvent → memory_written: false on EVERY ECS task; cross-task learning silently no-ops on the ECS-only fork (live-caught on ABCA-691). The AgentCore runtime and the orchestrator both get this grant via agentMemory.grantReadWrite; the ECS task role did not. This was fixed once before (c23d49f) on a side-branch that never merged to linear-vercel, so a redeploy from trunk silently reverted it — this lands it on the branch the platform actually deploys from. - EcsAgentCluster gains an optional `agentMemory` prop and grants the task role read+write when wired (mirrors agent.ts's grant to the runtime). - agent.ts passes agentMemory to the ECS cluster. - Tests: assert the bedrock-agentcore write grant is present when agentMemory is wired, AND a negative proof that no such grant exists when it is omitted (so the positive assertion can't pass vacuously — the exact gap that let this regress silently). Full build green (cdk 3098 tests).
…) + readable pytest Two dogfood findings from the ABCA-691 run, plus a latent classifier bug the log exposed. 1) PREVENT the OOM (K14). `mise run build` fans out its four packages in parallel (agent:quality ‖ cdk:build ‖ cli:build ‖ docs:build), each spawning its own worker fleet; the MEASURED driver of the 32/64/120 GB OOMs is that cross-package storm summing on top of the still-resident coding agent — not any one package. At 120 GB (Fargate's max at 16 vCPU) there is no more RAM, so the documented remedy is to cut peak parallelism: set MISE_JOBS=1 on the build task def so the four packages run SEQUENTIALLY (peak ≈ max-of-one, not sum-of-four) while every package still builds and both gates (baseline + post-agent) stay intact. Within-package parallelism (JEST_MAX_WORKERS=4, pytest) is untouched. Cost is wall-clock, trivial vs BUILD_VERIFY_TIMEOUT_S=3600. 2) CLASSIFY an OOM honestly. A bare SIGKILL (137) was NOT treated as infra unless stderr also carried an OOM string — but the cgroup OOM-killer writes to the KERNEL log, not the build's stderr, so an OOM'd build exits 137 with no such string. On ABCA-691 that 137 was mislabeled INERT (a greedy mise+"not found" heuristic matched an unrelated webhook-test fixture line); a 137 with plain output would have fallen through to a GENUINE build FAILURE → a false gate on healthy code (and a poisoned dependent cascade). is_infra_failure now treats a bare 137 as infra (a healthy build never SIGKILLs itself — a real test failure exits 1/2), checked before the inert/failure paths. Tests cover both the mislabeled-inert and the false-failure fall-through. 3) READABLE test output. pytest's default dot progress + easily-lost summary made an agent burn ~6 turns re-running the suite to confirm it passed under capture. addopts=-ra + console_output_style=count → explicit "N/M passed" and a trailing non-pass summary in any non-TTY log (CI, ECS gate, agent Bash). Display-only; never changes results. Full build green (cdk 3099 + agent quality).
…updates (aws-samples#592) Bumps the all-python group with 3 updates in the /agent directory: [boto3](https://github.com/boto/boto3), [bedrock-agentcore](https://github.com/aws/bedrock-agentcore-sdk-python) and [uvicorn](https://github.com/Kludex/uvicorn). Updates `boto3` from 1.43.38 to 1.43.40 - [Release notes](https://github.com/boto/boto3/releases) - [Commits](boto/boto3@1.43.38...1.43.40) Updates `bedrock-agentcore` from 1.16.0 to 1.17.0 - [Release notes](https://github.com/aws/bedrock-agentcore-sdk-python/releases) - [Changelog](https://github.com/aws/bedrock-agentcore-sdk-python/blob/main/CHANGELOG.md) - [Commits](aws/bedrock-agentcore-sdk-python@v1.16.0...v1.17.0) Updates `uvicorn` from 0.49.0 to 0.50.0 - [Release notes](https://github.com/Kludex/uvicorn/releases) - [Changelog](https://github.com/Kludex/uvicorn/blob/main/docs/release-notes.md) - [Commits](Kludex/uvicorn@0.49.0...0.50.0) --- updated-dependencies: - dependency-name: boto3 dependency-version: 1.43.40 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-python - dependency-name: bedrock-agentcore dependency-version: 1.17.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-python - dependency-name: uvicorn dependency-version: 0.50.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-python ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…ted failure, don't diagnose it
The ABCA-662 work re-titled a max_turns failure as "Ran out of turns retrying a
failing step". That over-claims: the stuck-guard's trailing window is only the last
~6 tool calls, which genuinely CANNOT distinguish a hard blocker (bad creds, no
permission — more turns won't help) from a long task that made real progress and
hit a recoverable snag only at the tail (more turns / a retry WOULD help — 662
itself: siblings pushed fine with the same token). Framing the whole run around its
last 6 calls misrepresents the latter.
- error-classifier: drop the separate "retrying a failing step" bucket. A max_turns
failure stays the plain "Exceeded max turns"; the copy points the reader at the
observed detail and still surfaces the environment-blocker path, but asserts NO
cause.
- stuck_guard.recent_failure_summary: emit a NEUTRAL observation ("last tool calls
repeated: `<cmd>` → <err>") instead of "spinning on failing tool calls". The
mid-run steer TO the agent (an advisory nudge, not a user surface) still says
"spinning" — it's coaching the agent, not classifying the outcome.
- tests pinned to the neutral wording + assert the title is NOT re-framed.
…ation + log delivery (aws-samples#595) * fix(bootstrap): grant route53resolver:UpdateFirewallRuleGroupAssociation to exec role Live-caught deploying the ECS substrate to dev (--context compute_type=ecs): the CFN execution role got AccessDenied on route53resolver:UpdateFirewallRuleGroup- Association, failing DnsFirewall/RuleGroupAssociation and rolling back the stack. The bootstrap policy already grants Associate/Disassociate/Get/List on the firewall rule-group association but not Update. That action is only exercised when CFN does an in-place UPDATE of the existing association (e.g. coming out of a rollback, or any change that rewrites the resource) — a fresh create or a no-op update never hits it, which is why it went unnoticed until now. Classic least-privilege bootstrap drift (same class as aws-samples#402/aws-samples#404/aws-samples#407/aws-samples#409), NOT an ECS- or branch-specific issue: main lacks the action too; deploying the ECS path just exercised the update path that surfaces it. Not added: the non-fatal s3:Get*/TagResource read-sweep denials CFN's S3 handler emits — those are tolerated (no S3 resource failed), and granting describe-handler breadth for unused bucket features would bury real gaps (the aws-samples#406 lesson). Regenerated bootstrap artifacts + updated the DEPLOYMENT_ROLES.md golden and its Starlight mirror. Full cdk suite green (125 suites / 2289 tests). (cherry picked from commit f69f01a) * fix(bootstrap): grant logs:UpdateDeliveryConfiguration to exec role Live-caught deploying to dev: the CFN execution role got AccessDenied on logs:UpdateDeliveryConfiguration updating RuntimeApplicationLogsDelivery (AWS::Logs::Delivery), which failed the stack update AND its rollback → the stack landed in UPDATE_ROLLBACK_FAILED. The observability bootstrap policy grants CreateDelivery + the LogDelivery family (Create/Get/Update/DeleteLogDelivery) + Put/Get/Delete DeliverySource/Destination, but not UpdateDeliveryConfiguration. That action is only exercised when CFN does an IN-PLACE update of an existing AWS::Logs::Delivery resource — a fresh create never hits it, so it went unnoticed until a stack that already had the delivery resource was updated. Same least-privilege bootstrap-drift class as aws-samples#402/aws-samples#404/aws-samples#407/aws-samples#409 and the route53resolver:UpdateFirewallRuleGroupAssociation fix on this branch. NOT an ECS- or branch-specific gap: verified logs:UpdateDeliveryConfiguration is absent on main and linear-vercel too, and this branch does not touch the delivery resource or observability.ts — the ECS deploy-verify merely exercised the update path that surfaces the latent hole. Regenerated bootstrap artifacts + updated the DEPLOYMENT_ROLES.md golden and its Starlight mirror. Full cdk suite green (125 suites / 2289 tests). (cherry picked from commit 1697c38)
… beforeEach (#115) (aws-samples#602) hydrateContext falls back to process.env.MEMORY_ID when no memoryId option is passed (context-hydration.ts:990). The test 'excludes memory_context when memoryId is not provided' asserted loadMemoryContext is NOT called — but if MEMORY_ID leaked into the jest process env (another test file, or the deploy env when the suite runs in-container), the source picked it up and DID call loadMemoryContext, failing the assertion. Order/environment-dependent flake: green locally (no MEMORY_ID), red on the fork's in-container build. Consequence: the flake blocked the pre-push hook, so the fork agent bypassed its own gate with `git push --no-verify` on BOTH ABCA-487 and ABCA-488. Fix: `delete process.env.MEMORY_ID` in beforeEach so every test starts from a clean no-ambient-memory baseline; tests that want memory pass the memoryId option explicitly (no test relies on the env fallback). Verified: the suite now passes 92/92 both with MEMORY_ID set in the env (the container condition) and without. Full build green (cdk 2853).
# Conflicts: # cdk/src/handlers/orchestrate-task.ts
…k-v1, not default/agent-v1 (aws-samples#594) * fix(workflows): repo-bound task w/o workflow_ref → coding/new-task-v1, not default/agent-v1 aws-samples#296 (workflow-driven tasks) replaced the task_type→workflow mapping with a resolution ladder, but left the repo-aware rung ('Phase 4') unwired. Every task with no explicit workflow_ref — all Slack tasks and all aws-samples#247 orchestration children (neither sets one) — fell through to the platform default default/agent-v1: the freeform, repo-less agent prompt with NO git/PR discipline. The agent then improvised (gh api / gh pr create against an empty local clone), so ensure_pr found no commits and recorded pr_url=null, screenshot→Linear routing lost its branch signal, and aws-samples#247 A4 stacking broke (children couldn't fetch the unpushed predecessor branch). Re-wire the missing rung minimally: resolveWorkflowRef takes hasRepo; an absent ref with a repo present resolves to coding/new-task-v1 (the disciplined coding workflow — edit locally, commit, push, platform opens the PR via ensure_pr), matching pre-aws-samples#296 behaviour. Repo-less tasks still default to default/agent-v1. An explicit workflow_ref always wins. The single create-task-core call site passes Boolean(body.repo), so both Slack and orchestration (which create via createTaskCore with repo set) inherit the fix. Upstream regression — worth an upstream issue too. Updated workflows + create-task-core tests; the old test asserting default/agent-v1 for a repo task encoded the regression. (cherry picked from commit 99b5a17) (cherry picked from commit 9d8b25d) * refactor: pin coding workflow at channel call sites, not the resolver default Rework per review (aws-samples#594): follow the merged aws-samples#547 precedent instead of changing the shared resolver default. A repo-bound channel task must run the disciplined coding workflow (clone → commit → push → platform opens the PR), not the repo-less default/agent-v1 that records pr_url=null — but that "repo task ⇒ coding workflow" decision now lives explicitly at each channel's createTaskCore call site rather than as an implicit resolver-level inference. Addresses the review point-by-point: - B3(a): revert resolveWorkflowRef to its single-arg form (no hasRepo); pin workflow_ref: CODING_WORKFLOW_ID in the Linear and Slack processors, mirroring the Jira processor (aws-samples#546/aws-samples#547). One visible decision per channel, uniform across all three. (No aws-samples#247-child path exists on main.) - B2: moot — no resolver behaviour change, so WORKFLOWS.md / API_CONTRACT.md / USER_GUIDE.md all stay accurate. No doc edits, no mirror regen needed. - N1: moot — the hasRepo param is gone, so the "forgot hasRepo" footgun can't exist. - N2: promote the 'coding/new-task-v1' literal to CODING_WORKFLOW_ID next to DEFAULT_WORKFLOW_ID, with a module-load invariant asserting both ids exist in DESCRIPTORS (a descriptor rename now fails at import, not as a runtime TypeError). Jira's literal switched to the constant too — uniform. - N3: moot — the stale "last rung" JSDoc was in the reverted resolver block. - §6 test gap: create-task-core caller tests now assert the real behaviour (omitted ref → default/agent-v1, with AND without a repo — the symmetric repo-less case that was missing). Channel processor tests (Linear/Slack/Jira) assert each pins workflow_ref: 'coding/new-task-v1'. Full cdk build green (compile + jest + eslint + synth).
…4) (aws-samples#601) * fix(fanout): render the PR link on ✅ success, not only on⚠️ (F-prlink) The Linear completion comment rendered the PR URL ONLY on the⚠️ "shipped a PR but stopped early" path, on the assumption that on ✅ success the agent's own step-2 "PR opened" comment reliably carries the link, so duplicating it is noise. That assumption fails when the agent skips its PR-opened comment. Live-caught on ABCA-584: a :decompose that declined to split ran as one coding task, opened PR aws-samples#395 (pr_url + build_passed on the task record), but posted NO "🔗 PR opened" comment — so the ✅ "Task completed" comment omitted the link and it was lost entirely; the user asked "where is the PR link?". Fix: render `PR: <url>` whenever the task produced one, on BOTH ✅ and⚠️ . The completion comment is the terminal, platform-owned surface, so it must carry the link rather than depend on the agent choosing to post its own; a duplicate with the agent's comment (when it does fire) is far cheaper than a missing PR, and this is the one terminal comment per task so it can't spam. Test updated to assert the link renders on ✅. Full cdk build green (2935 tests). Held. * docs+test: address review nits N1–N3 on the PR-link render Optional polish from the aws-samples#601 review (fix itself approved, unchanged): - N1: add a negative assertion for the branch this change now makes live — ✅ task_completed + prUrl null → NO `PR:` line. The relaxed `if (args.prUrl)` guard means this is no longer structurally guaranteed by the old⚠️ -only condition, so pin its absence (fanout-task-events.test.ts). - N2: reword the "ONE terminal comment per task" comment — the single-post guarantee lives in the caller `dispatchToLinear` (linear_final_comment_event_id idempotency marker), NOT this pure formatter, which enforces nothing. Credit the right layer so a future non-idempotent caller isn't misled. - N3: add a one-line note atop the function JSDoc — prUrl, when present, renders on ALL frames — so the "why" isn't split between the JSDoc (ABCA-91 story) and the render-site note (ABCA-584) 50 lines below. No behavior change. fanout-task-events tests green (108); eslint clean.
…aws-samples#573) (aws-samples#603) * feat(jira): post final status comments with cost, turns, and duration (aws-samples#573) Mirror the Linear final-status fan-out for Jira. Jira-origin terminal tasks now get a deterministic platform-side comment with outcome, cost, turns, duration, task id, and the PR link — posted by the fan-out plane, not the agent, so it fires even when the agent crashes before completing (max-turns, OOM). - fanout: add `jira` NotificationChannel + CHANNEL_DEFAULTS (terminal events + task_timed_out) + `dispatchToJira`, structurally mirroring `dispatchToLinear` (env guard -> load task -> channel_source gate -> metadata read -> post-once marker -> post). New renderer `renderJiraFinalStatusComment` emits ADF paragraphs with the same three-outcome framing (completed / shipped-but-stopped / failed); the PR link renders on the success path too (Jira has no other surviving PR-link surface once the agent terminal comment is demoted). - jira-feedback: add `buildAdfDocument` (multi-paragraph ADF with strong/em marks) + `postIssueCommentAdf` returning a classified `JiraPostResult` (retryable vs terminal), reusing the 401 forced- refresh-and-retry-once path. `postIssueComment` keeps its boolean API. - TaskRecord: add `jira_final_comment_event_id` post-once marker; TaskNotificationsConfig gains a `jira` channel. - construct/stack: wire JiraWorkspaceRegistryTable + `bgagent-jira-oauth-*` Get/Put grant into FanOutConsumer (guarded). - agent: demote the Jira terminal comment — remove `comment_task_finished` and its two pipeline call sites so fanout owns the terminal comment (no double-post); keep the start comment. - docs: update JIRA_SETUP_GUIDE for the platform-side final comment. Tests: fanout Jira success / failure / shipped-but-stopped / timed_out / missing-metadata / non-Jira skip / idempotent retry / retryable escalation / marker persistence / missing-env; ADF builder + classified post result; construct grant guards; agent terminal-comment-demoted guard. * feat(jira): render the PR link in the final-status comment as a clickable hyperlink ADF — unlike Linear's Markdown — does not auto-linkify a bare URL in a plain text node, so the PR link in the Jira final-status comment rendered as unclickable text the requester had to copy-paste. Add an optional `href` to `AdfTextRun`; `buildAdfDocument` maps it to an ADF `link` mark (composes with strong/em). The Jira final-status renderer splits the PR line into a "PR: " label run + a URL run carrying `href`, so it renders as a real hyperlink on both the ✅ and⚠️ paths. Tests: buildAdfDocument link-mark emission + href/strong composition; renderer asserts the URL run carries the href. * fix(jira): address aws-samples#603 review — reframe ✅ PR-link rationale, humanize task_timed_out header, match Linear bold scope Review feedback on aws-samples#603 (all from isadeks' approve-with-notes): 1. Drop the "diverges from Linear" framing for rendering the PR link on the ✅ success path. The real reason is general (ABCA-584): the agent's own "PR opened" comment isn't guaranteed to fire, so the platform comment must always carry the link. aws-samples#601 lands the same fix for Linear, so this is no longer a Jira-specific divergence — reframed the docstring + renamed the test accordingly to avoid a stale-comment / semantic merge conflict with aws-samples#601. 2. Humanize the ❌ header subtype: strip `task_` AND turn underscores into spaces so `task_timed_out` renders "Task timed out", not the raw "Task timed_out". Jira is the only channel routing task_timed_out through this renderer, so it's a case the copied-from-Linear code never hit. Added a test. 4. Match Linear's⚠️ bold scope: bold only through the reason and leave the trailing "— review and decide…" advice unbolded (two runs), instead of bolding the whole header. Added a test. (#3 — adding task_timed_out to CHANNEL_DEFAULTS.linear for parity — is an optional follow-up the reviewer flagged as out of scope; tracked separately.) --------- Co-authored-by: Sphia Sadek <isadeks@gmail.com>
When `build`/`integ` complete on a PR, aggregate all check-runs + commit statuses and either (a) comment which checks are failing (no review compute spent), (b) auto-update an out-of-date branch so CI re-runs (PAT so build re-fires), (c) comment asking to resolve conflicts, or (d) trigger the ABCA coding/pr-review-v1 agent via the Task API webhook once green. Comments-only, edit-in-place, per-SHA idempotent — does not touch Mergify or the integ-smoke gate. Runs in the trusted base-repo context via workflow_run so secrets/PAT are available for fork PRs; no PR code is checked out or executed. Adds review-gate.yml to .github/zizmor.yml dangerous-triggers and secrets-outside-env ignore lists (intentional workflow_run + repo-level secrets; the gate has no environment/approval boundary).
…aws-samples#502) (aws-samples#503) * fix(ecs): write task payload to S3, not inline overrides (aws-samples#502) The ECS compute strategy inlined the full orchestrator payload (incl. the large hydrated_context) into the AGENT_PAYLOAD container-override env var. ECS RunTask caps the TOTAL containerOverrides blob at 8192 bytes, so any real task was rejected before the container started: InvalidParameterException: Container Overrides length must be at most 8192 AgentCore is unaffected — it passes the payload in the InvokeAgentRuntime request body, which has no comparable limit. The bug only surfaces with a realistic hydrated payload, which is why the prior ECS smoke test (a small Rust cargo-check, aws-samples#494) didn't catch it. Fix — stash the payload out-of-band and pass only a pointer: - New EcsPayloadBucket construct (mirrors TraceArtifactsBucket): BLOCK_ALL, enforceSSL, S3_MANAGED encryption, 1-day lifecycle TTL (payloads are ephemeral — read once at boot). Dedicated bucket so the ECS task role's S3 read is scoped to payloads only and can't touch attachments/traces. - ecs-strategy: when ECS_PAYLOAD_BUCKET is set, PutObject the payload to <task_id>/payload.json and pass AGENT_PAYLOAD_S3_URI in the override; the boot command fetches+parses it via boto3. Inline AGENT_PAYLOAD remains as a fallback (small payloads / no bucket), so nothing regresses. deleteEcsPayload helper removes the object. - orchestrate-task finalize: best-effort deleteEcsPayload for ECS tasks once terminal (the container has long since read it); lifecycle rule is the crash backstop. - EcsAgentCluster: accept payloadBucket, inject ECS_PAYLOAD_BUCKET env, grant the task role READ ONLY (untrusted repo code must not write/delete payloads; the trusted orchestrator owns write+delete). Session-role-aware. - task-orchestrator: ecsPayloadBucket prop → grantPut + grantDelete to the orchestrator; @aws-sdk/client-s3 added to bundling externals. - agent.ts: updated the commented uncomment-to-enable ECS scaffolding to wire the payload bucket. Tests: new bucket construct (TTL/SSL/block-public/autoDelete); strategy S3-write + URI-pointer + inline fallback + deleteEcsPayload (incl. best-effort swallow + no-op without bucket); cluster read-grant + env var + read-only (no put/delete). Full build green. Closes aws-samples#502 * fix(ecs): remove dead ECS_PAYLOAD_OBJECT_KEY_PREFIX export (review B1) The `export const ECS_PAYLOAD_OBJECT_KEY_PREFIX = ''` constant was referenced nowhere — ecsPayloadKey() (ecs-strategy.ts) builds `<task_id>/payload.json` independently and already documents that layout in its own docstring. The dead export was a "prefix" whose value was '' with a docstring describing a key layout it didn't produce, and it raised the knip dead-code count, tripping the ratchet inside `mise run build`. Delete it; ecsPayloadKey remains the single source of truth for the key layout. --------- Co-authored-by: bgagent <bgagent@noreply.github.com> Co-authored-by: Alain Krok <alkrok@amazon.com>
Operator walkthrough for wiring the review-gate workflow: register an ABCA webhook (bgagent webhook create), set the ABCA_TASK_API_URL / ABCA_WEBHOOK_ID repo vars and ABCA_WEBHOOK_SECRET secret, confirm the workflow is on the default branch, and smoke-test. Covers the decision tree, fork-vs-same-repo update behavior, opting PRs out, and troubleshooting (401/403, not-onboarded, green-but-no-review, dismissed approvals). Mirrors to using/ via sync-starlight (script entry + link route + sidebar slug), and links from USER_GUIDE's webhook bullet. Docs build verified.
…gress (aws-samples#572) (aws-samples#605) * feat(jira): transition originating issue across workflow as tasks progress Move the originating Jira issue through its workflow so the board reflects the task lifecycle: To Do → In Progress on start, → In Review on PR. Best-effort only — logged and swallowed on any failure, sharing the existing 401/403 auth circuit breaker, so the board never fails, blocks, or retries the task. Failed tasks and tasks with no PR leave the status unchanged. Status resolution ladder (per lifecycle point): per-project override (--status-on-start / --status-on-pr) → safe heuristic (statusCategory `indeterminate` on start, `In Review` name-match on PR) → skip with a warning. Transitions requiring a screen and empty transition lists (no Transition Issues permission) are skipped. No new OAuth scopes — read:/write:jira-work cover both the transitions GET and POST. - agent/src/jira_reactions.py: add transition_task_started / transition_pr_opened best-effort helpers; extract shared circuit-breaker accounting. - agent/src/pipeline.py: invoke helpers at the start hook and the success-path PR hook (only when a PR was opened). - cdk/src/handlers/jira-webhook-processor.ts: stamp optional jira_status_on_start / jira_status_on_pr from the project mapping into channel_metadata. - cli/src/commands/jira.ts: add optional --status-on-start / --status-on-pr to `bgagent jira map`. - docs/guides/JIRA_SETUP_GUIDE.md: Board transitions section (defaults, overrides, Transition Issues permission prerequisite) + troubleshooting. Closes aws-samples#572 * fix(jira): address review — crash-safe transitions, Linear-parity selection Addresses isadeks' review on PR aws-samples#605. 1. (correctness) _get_issue_transitions no longer raises AttributeError on valid-but-non-object JSON (null / bare list / scalar). Previously resp.json().get(...) could raise past the best-effort boundary, propagate out of the pipeline hook, and flip an otherwise-successful task to FAILED (worst case: after the PR opened). Now guarded with isinstance(dict). 2/3. (Linear parity) Rework transition selection to mirror the Linear reference (prompt_builder.py): - Start prefers a destination NAMED "In Progress" before the indeterminate category fallback — fixes the order-dependent bug where "Blocked" (also indeterminate) could be picked. The category fallback now excludes "Blocked". - PR prefers "In Review" then synonyms (Code Review / Review / Peer Review / Reviewing) then falls back to "In Progress", so a stock board or a Code-Review column isn't silently skipped. - Skip if the issue is already at/past the target category (won't drag a card backward on re-trigger). Fetches ?fields=status&expand=transitions to get current status + transitions in one call. Overrides bypass the already-past skip (deliberate instruction). 4. (hardening) CLI trims --status-on-start/--status-on-pr and treats whitespace-only as unset, so a fat-fingered blank can't persist a permanent no-op. Tests: agent 43 (adds Blocked-vs-InProgress order, already-past skips, PR synonym + In-Progress fallback, non-dict JSON parametrized); CLI adds trim + whitespace-unset. Docs: resolution ladder rewritten to match. Full build green.
A Linear task against the full ABCA monorepo wedged silently for 50+ min: the pre-agent baseline build (mise run build → the agent pytest suite) hung on the ECS substrate, and the issue showed no reaction/comment/state the whole time. Root-caused live on ABCA-707. Three fixes: 1. Kill the hang at its root (agent/tests/conftest.py). The ECS agent task def sets AGENT_SESSION_ROLE_ARN. With it set, aws_session resolves a *scoped* session and tenant_client returns session.client(...), which BYPASSES a @patch("boto3.client") mock. test_attachments then makes a REAL S3 get_object that blocks forever on the ECS network (no egress) in a socket read SIGALRM can't interrupt. Add AGENT_SESSION_ROLE_ARN to the _clean_env scrub list so every test resolves the unscoped path where the mock intercepts. reset_session_cache() alone was insufficient — a cold get_session() re-resolves scoped while the var is still set. + regression test (TestConftestScrubsScopingEnv). 2. Make the hang watchdog actually reap (agent/tests/conftest.py). The prior faulthandler.dump_traceback_later(1200, exit=True) never fired: pytest's faulthandler_timeout re-arms faulthandler's single timer per-test WITHOUT exit=True, cancelling the session-level exit timer. Replace with an independent daemon-thread reaper that dumps all stacks and os._exit(1)s at 600s, so a future hang fails the build fast instead of burning to the 3600s ceiling. 3. Early Linear ACK (agent/src/pipeline.py). Move token resolve + 👀 reaction + Backlog→In Progress + start comment to BEFORE setup_repo() so a large-repo task shows immediate feedback during the multi-minute baseline build instead of looking dead. As a side benefit, a setup-phase failure now has a 👀 for the existing outer crash handler to swap to ❌ — closing the silent-stuck-issue gap. configure_channel_mcp stays after the clone (it needs the repo dir).
aws-samples#614) A follow-up `@bgagent <request>` comment on a completed Linear task that opened no PR was silently dropped. handleStandaloneCommentTrigger is iteration-only: on prNumber === null it checks for a clarify-hold and, if it isn't one, returns with just an info log — the comment vanished. But a task finishes PR-less in several real ways (no change needed, failed-before-commit, or a question/investigation run), and a follow-up on such an issue is almost always NEW work ("then just do X instead"), not iteration. Add maybeStartStandaloneNewWork: when the repo + user are known and the comment carries instruction text, dispatch a fresh coding/new-task-v1 against the same repo using the comment text as the description — reusing the same 👀-ack + threaded reply + fanout terminal ownership as the iteration and clarify-resume paths. Idempotency keyed newwork_<issue>_<comment>. Edge cases: a bare `@bgagent` (no instruction) gets a threaded reply telling the user what to do rather than a vague dispatch or silence; no repo / no user_id falls through to the (now-narrowed) no-op log. Tests: replace the obsolete 'NO PR → no task, no ack' case with the new-work dispatch, bare-mention reply, no-repo, and no-user cases. Full cdk build green (3118 tests). Closes aws-samples#614.
…to-lv-2026-07-15 # Conflicts: # agent/src/pipeline.py # cdk/src/constructs/ecs-agent-cluster.ts # cdk/src/constructs/ecs-payload-bucket.ts # cdk/src/handlers/fanout-task-events.ts # cdk/src/handlers/shared/strategies/ecs-strategy.ts # cdk/src/handlers/shared/workflows.ts # cdk/src/stacks/agent.ts # cdk/test/constructs/ecs-agent-cluster.test.ts # cdk/test/handlers/shared/create-task-core.test.ts # cdk/test/handlers/shared/strategies/ecs-strategy.test.ts # cdk/test/handlers/shared/workflows.test.ts
…rror aws-samples#596 B1) Mirrors the aws-samples#596 review B1 fix onto linear-vercel (this defect rode in via the main→lv merge a9efaa6, which kept lv's version of the grant). coding/decompose-v1 delivers its plan via the assumed SessionRole (scoped to artifacts/${task_id}/*), exactly like the AgentCore runtime whose task role has no direct artifacts grant. The whole-bucket grantReadWrite over-privileged the untrusted-code role and broke cross-task isolation. Task role keeps only the ARTIFACTS_BUCKET_NAME env; corrected the inverted comment + the stale artifacts clause in the cdk-nag IAM5 reason. Test flipped to assert the task role has NO s3:Put*/Delete* action. Full cdk build green.
The Task API validates Idempotency-Key against ^[a-zA-Z0-9_-]{1,128}$, but the
gate built it as `review-$REPO-$PR_NUMBER-$HEAD_SHA` where $REPO is `owner/repo`
— the "/" fails validation (and owner/repo + a 40-char SHA can exceed 128), so
the webhook returned 400 VALIDATION_ERROR and no review was ever triggered on a
green PR. Map disallowed chars to "-" and cap at 128; still per-(repo,PR,SHA)
unique. Verified live: bad key → 400, sanitized key → 201 (task created).
This reverts commit 4466a02.
This reverts commit a746a9d.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Task-Id: 01KXTWWTTKDKFC4FCT07MM7DC0 Prompt-Version: 1c9c10e027a2
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Task-Id: 01KXTXPCFV81ZDQG7VZET4PQ6E Prompt-Version: 1c9c10e027a2
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Task-Id: 01KXTYGH0J1Z6YY1G83MQAGTDY Prompt-Version: 1c9c10e027a2
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Creates
docs/stress-ecs/z.mdcontaining a single line:z ok.Resolves ABCA-727 ([chain-Z]).
Notes
mainincludes the preceding x.md/y.md commits until they merge.--no-verify.🤖 Generated with Claude Code